This is an R-code companion to Session 7, and the contents here
encompass just about all the material in that session. During the
lecture associated with the lesson, we showed some OpenGL-based
animations of the MCMC sampler for the inbreeding model, but the code
for that is implemented in C, and is not super accessible. So, the goal
here is to implement, in R, the model and MCMC for the model using the
several different types of samplers discussed in the session.
Let’s load Hadley’s tidyverse and other packages we need before we
get going. The following will also install them if you don’t have
them.
packs <- c("tidyverse", "plotly", "viridis", "broom")
# install any that are not here
install_em <- setdiff(packs, rownames(installed.packages()))
if (length(install_em) > 0) install.packages(pkgs = install_em)
# load em up
dump <- lapply(packs, function(x) library(x, character.only = TRUE))
Registered S3 methods overwritten by 'dbplyr':
method from
print.tbl_lazy
print.tbl_sql
── Attaching packages ─────────────────────────────────────────────────────────── tidyverse 1.3.1 ──
✔ ggplot2 3.3.6 ✔ purrr 0.3.4
✔ tibble 3.1.7 ✔ dplyr 1.0.9
✔ tidyr 1.2.0 ✔ stringr 1.4.0
✔ readr 2.1.2 ✔ forcats 0.5.1
── Conflicts ────────────────────────────────────────────────────────────── tidyverse_conflicts() ──
✖ dplyr::filter() masks stats::filter()
✖ dplyr::lag() masks stats::lag()
Registered S3 method overwritten by 'data.table':
method from
print.data.table
Registered S3 methods overwritten by 'htmltools':
method from
print.html tools:rstudio
print.shiny.tag tools:rstudio
print.shiny.tag.list tools:rstudio
Registered S3 method overwritten by 'htmlwidgets':
method from
print.htmlwidget tools:rstudio
Attaching package: ‘plotly’
The following object is masked from ‘package:ggplot2’:
last_plot
The following object is masked from ‘package:stats’:
filter
The following object is masked from ‘package:graphics’:
layout
Loading required package: viridisLite
The joint probability
As discussed in the previous session, this is just the product of the
priors and the likelihoods. In its full glory, it looks like. (For a
review of how that joint density was developed, see the discussion at the very end \[
\begin{aligned}
P(n_{AA}, n_{Aa}, n_{aa}, f, p | \alpha_f, \beta_f, \alpha_p, \beta_p)
& =
\frac{\Gamma(\alpha_f +
\beta_f)}{\Gamma(\alpha_f)\Gamma(\beta_f)}f^{\alpha_f - 1}(1-f)^{\beta_f
- 1}
\frac{\Gamma(\alpha_p +
\beta_p)}{\Gamma(\alpha_p)\Gamma(\beta_p)}p^{\alpha_p - 1}(1-p)^{\beta_p
- 1} \times \\
& \frac{n!}{n_{AA}! + n_{Aa}! + n_{aa}!}~
[fp + (1-f)p^2]^{n_{AA}}~
[(1-f)2p(1-p)]^{n_{Aa}}~
[f(1-p) + (1-f)(1-p)^2]^{n_{aa}}
\end{aligned}
\] We will be using this in MCMC in which the the hyperpriors
\((\alpha_f, \beta_f, \alpha_p,
\beta_p)\) and the data are fixed. Thus the terms (factors) that
involve only the data and the hyperpriors are constants that
will drop out of the Hastings ratio, so they can be safely ignored. So,
our joint probability can be computed as something that looks like:
\[
\begin{aligned}
P(n_{AA}, n_{Aa}, n_{aa}, f, p | \alpha_f, \beta_f, \alpha_p, \beta_p)
& \propto
f^{\alpha_f - 1}(1-f)^{\beta_f - 1}
p^{\alpha_p - 1}(1-p)^{\beta_p - 1} \times \\
& ~~~~[fp + (1-f)p^2]^{n_{AA}}~
[(1-f)2p(1-p)]^{n_{Aa}}~
[f(1-p) + (1-f)(1-p)^2]^{n_{aa}}
\end{aligned}
\] We will implement this in a function that returns the
log of the above quantity so that we won’t run into underflow
issues with very large samples:
#' the log of the joint probability of all the data and paramaters given the priors
#'
#' This is computed having dropped all the constant factors in the probability.
#' @param n a vector of three elements: (nAA, nAa, naa), the number of AA's, the
#' number of Aa's, and the number of aa's
#' @param f the inbreeding coeffient
#' @param p the frequency of the A allele
#' @param pri a vector of four components: (alpha_f, beta_f, alpha_p, beta_p) which are the
#' beta parameters for the priors on f and p. By default they are all 1.
log_joint_prob <- function(n, f, p, pri = c(1, 1, 1, 1)) {
# not going to do much error checking here
# compute the raw values, anything outside of the unit interval we will send
# back as NaN
ifelse(f <= 0 | f >= 1 | p <= 0 | p >= 1, NaN,
log(f) * (pri[1] - 1) +
log(1 - f) * (pri[2] - 1) +
log(p) * (pri[3] - 1) +
log(1 - p) * (pri[4] - 1) +
log(f * p + (1 - f) * p ^ 2) * n[1] +
log((1 - f) * 2 * p * (1 - p)) * n[2] +
log(f * (1 - p) + (1 - f) * (1 - p) ^ 2) * n[3])
}
Note that this function is vectorized over \(f\) and \(p\), but not \(n\) or the priors.
A 3-D plot
We can compute the joint probability of all the variables easily,
and, because this is a toy problem in two dimensions, we can visualize
roughly what the posterior surface will look like by computing values on
a grid and normalizing those values so they sum to one. Let’s do that,
since it is a good chance to introduce the plotly package to anyone that might not
yet know it.
So, let’s take the example from the lecture and set our data to be
\(n_{AA} = 30\), \(n_{Aa} = 10\), and \(n_{aa} = 10\). Those are the expected
values if \(f = 0.5\) and \(p = 0.7\),
ndata <- c(30, 10, 10)
and then compute the log prob, exponentiate it, and normalize it over
a grid of value for \(f\) and \(p\):
f_vals <- seq(0, 1, by = 0.03)
p_vals <- seq(0, 1, by = 0.03)
# make a tidy tibble of values
grid_vals <- expand.grid(f = f_vals, p = p_vals) %>%
as_tibble() %>%
mutate(joint = exp(log_joint_prob(ndata, f, p))) %>%
mutate(norm_joint = joint / sum(joint, na.rm = TRUE)) %>%
select(-joint)
# then put those values into a matrix, with rows corresponding to f and cols to p
probs <- matrix(grid_vals$norm_joint, nrow = length(f_vals), ncol = length(p_vals))
Now we can plot it with plotly:
plot_ly(x = ~p_vals, y = ~f_vals, z = ~probs) %>%
add_surface()
Try interacting with that plot, especially if you are not familiar
with plotly. You can zoom, pan, and revolve it, and you can also look at
individual values by mousing over within the plot.
“2-D” Metropolis-Hastings Sampler
We now start our forays into different MCMC samplers, roughly
following the lecture notes from the session. The first is a sampler in
which we propose changes to both \(f\)
and \(p\) and then we accept or reject
both of those proposaled changes, together, in one fell swoop, according
to the Hastings Ratio.
For our proposals in this case we will use two independent,
symmmetrical normal distributions centered on the current values. We
will wrap up the whole process in a single function called
mcmc_2d_mh().
#' function to do MCMC using a "2-D" Metropolis-Hastings sampler
#' @param ndata a three-vector (nAA, nAa, naa) of the observed data
#' @param pri a vector of four components: (alpha_f, beta_f, alpha_p, beta_p)
#' @param init a two vector of starting values (f, p). Each should be within the
#' unit interval.
#' @param sweeps the number of sweeps (i.e proposed changes) to do in the MCMC chain.
#' @param f_sd the standard deviation of the normal distribution for proposing new values to f
#' @param p_sd the standard deviation of the normal distribution for proposing new values to p
#' @return returns a tibble of proposals made, values visited, values of MH ratio, etc
mcmc_2d_mh <- function(ndata,
pri = c(1, 1, 1, 1),
init = c(.2, .5),
sweeps = 500,
f_sd = 0.07,
p_sd = 0.07) {
# create vectors in which to store output. Note that we will be storing
# the proposed values and the MH-ratio each sweep, too, for later analysis.
f <- p <- fprop <- pprop <- mh_ratio <- rep(NA_real_, sweeps + 1)
accepted_f <- accepted_p <- rep(NA, sweeps + 1)
# put initial values in
f[1] <- init[1]
p[1] <- init[2]
# then cycle over sweeps
for (i in 1:sweeps) {
# propose new values
fprop[i] <- rnorm(1, f[i], f_sd)
pprop[i] <- rnorm(1, p[i], p_sd)
# compute the hastings ratio
# the log M-H ratio is the difference of the log joint probs.
# since the proposal distributions are symmetrical they drop out of the ratio.
mh_ratio[i] <- exp(log_joint_prob(ndata, fprop[i], pprop[i], pri) -
log_joint_prob(ndata, f[i], p[i], pri))
# now, if mh_ratio[i] is NaN it means that f or p was proposed outside of the unit interval
# and it should be rejected immediately. If not, then we simulate a uniform R.V. on (0, 1)
# and reject the proposal if that number is greater than the MH-ratio.
if (is.nan(mh_ratio[i]) || runif(1) > mh_ratio[i]) {# reject!
accepted_f[i] <- FALSE
accepted_p[i] <- FALSE
f[i + 1] <- f[i]
p[i + 1] <- p[i]
} else {# accept!
accepted_f[i] <- TRUE
accepted_p[i] <- TRUE
f[i + 1] <- fprop[i]
p[i + 1] <- pprop[i]
}
}
# in the end, make a tibble and return
tibble(sweep = 1:(sweeps + 1),
f = f,
p = p,
proposed_f = fprop,
proposed_p = pprop,
mh_ratio = mh_ratio,
accepted_f = accepted_f,
accepted_p = accepted_p
)
}
A brief note: In the above function (and the one
following) we are being careful to record not just the current state of
the chain, but also every proposed value. Typically this is not
done in MCMC. Rather, it is more usual to simply record the state of the
chain at each sweep (or at some “thinned” interval of sweeps). However
we wanted to keep a full record of the progression of all the variables
so that they would be available for students to peruse or analyze.
Component-wise Metropolis-Hastings Sampler
Rather than proposing changes to both \(f\) and \(p\) before accepting or rejecting them, we
can propose just a change to \(f\)
(say) and accept or reject the proposal, and then propose just a change
to \(p\), and then accept or reject
that. This is called component-wise Metropolis-Hastings sampling.
Underlying it is a central theme in MCMC, which is that just one or a
few dimensions can be changed with each proposal, but as long as the
acceptance or rejection of each proposal is done in a way that satisfies
detailed balance, and as long as all the different types of
proposals, when used together create an irreducible chain around the
space, then you get a valid MCMC sampler.
We make a function that looks much like mcmc_2d_mh(),
but in which the proposal and acceptance steps for \(f\) and \(p\) are done separately within each sweep.
This is going to look a little bit weird because we do each update
within the sweep but set the new value of \(f\) in the “next” sweep. (You’ll see
below…)
#' function to do MCMC using a component-wise Metropolis-Hastings sampler
#' @param ndata a three-vector (nAA, nAa, naa) of the observed data
#' @param pri a vector of four components: (alpha_f, beta_f, alpha_p, beta_p)
#' @param init a two vector of starting values (f, p). Each should be within the
#' unit interval.
#' @param sweeps the number of sweeps (i.e proposed changes) to do in the MCMC chain.
#' @param f_sd the standard deviation of the normal distribution for proposing new values to f
#' @param p_sd the standard deviation of the normal distribution for proposing new values to p
#' @return returns a tibble of proposals made, values visited, values of MH ratio, etc
mcmc_cw_mh <- function(ndata,
pri = c(1, 1, 1, 1),
init = c(.2, .5),
sweeps = 500,
f_sd = 0.07,
p_sd = 0.07) {
# create vectors in which to store output. Note that we will be storing
# the proposed values and the MH-ratio each sweep, too, for later analysis.
f <- p <- fprop <- pprop <- mh_ratio_f <- mh_ratio_p <- rep(NA_real_, sweeps + 1)
accepted_f <- accepted_p <- rep(NA, sweeps + 1)
# put initial values in
f[1] <- init[1]
p[1] <- init[2]
# then cycle over sweeps
for (i in 1:sweeps) {
# propose new value for f
fprop[i] <- rnorm(1, f[i], f_sd)
# compute the hastings ratio with only a change to f proposed
mh_ratio_f[i] <- exp(log_joint_prob(ndata, fprop[i], p[i], pri) -
log_joint_prob(ndata, f[i], p[i], pri))
# then accept or reject it
U <- runif(1)
if (is.nan(mh_ratio_f[i]) | runif(1) > mh_ratio_f[i]) {# reject!
accepted_f[i] <- FALSE
f[i + 1] <- f[i]
} else {# if accepted, then update the value of f
accepted_f[i] <- TRUE
f[i + 1] <- fprop[i] # set the value. This is a little weird because we are still in sweep i.
}
# now, the current value of f is stored in f[i + 1], and we propose a new value for p
pprop[i] <- rnorm(1, p[i], p_sd)
# compute the hastings ratio using the new f and the proposed value of p
mh_ratio_p[i] <- exp(log_joint_prob(ndata, f[i + 1], pprop[i], pri) -
log_joint_prob(ndata, f[i + 1], p[i], pri))
# and reject or accept the proposal
if (is.nan(mh_ratio_p[i]) | runif(1) > mh_ratio_p[i]) {# reject!
accepted_p[i] <- FALSE
p[i + 1] <- p[i]
} else {# if not accepted then make p the same as the current p
accepted_p[i] <- TRUE
p[i + 1] <- pprop[i]
}
}
# in the end, make a tibble and return
tibble(sweep = 1:(sweeps + 1),
f = f,
p = p,
proposed_f = fprop,
proposed_p = pprop,
mh_ratio_f = mh_ratio_f,
mh_ratio_p = mh_ratio_p,
accepted_f = accepted_f,
accepted_p = accepted_p
)
}
Using the samplers
Each of these Metropolis-Hastings samplers returns output in a
tibble. For example:
mcmc_cw_mh(ndata = c(30, 10, 10))
Exercises
- Run each of the samplers for 5000 sweeps, with
ndata = c(30, 10, 10).
- Plot the traces for \(f\) and \(p\) for both the 2-d and the componentwise
sampler. Are they visibly different?
- Compare the acceptance rates for \(p\) and \(f\) for both samplers.
- In 3 you should have found that the acceptance rate is higher in the
component-wise sampler (as expected).
- This was a simple exercise in sampling continuous random variables
according to a two dimensional posterior surface. Now, imagine that the
standard deviations of the proposal distributions for \(p\) and \(f\) are fixed, then, draw (by hand) the
level curves (a “contour” plot) of a 2-d posterior surface for which the
2-d sampler might have a higher acceptance rate than the component wide
sampler. Draw the standard deviations of the proposal distributions on
for scale.
Review of the model
We are dealing with a very simple model for inbreeding. It is one
type of model that might be used to account for an excess of homozygotes
in a sample from a population. We assume that we have taken a sample of
\(n\) diploid individuals from the
population, and we will be considering just a single locus at a time.
This locus has two alleles \(A\) and
\(a\), and our sample can be summarized
by (what turns out to be the sufficient statistic, in this case) the
numbers of the three possible genotypes: \(n_{AA}\), the number of \(AA\) homozygotes; \(n_{Aa}\), the number of heterozygotes; and
\(n_{aa}\), the number of \(aa\) homozygotes. Of course, \(n_{AA} + n_{Aa} + n_{aa} = n\).
In a population with no inbreeding, the expected fraction of
genotypes follows the Hardy-Weinberg equilibrium proportions. Letting
\(p\) denote the frequency of the \(A\) allele in the population, under
Hardy-Weinberg equilibrium the, expected fractions of the different
genotypes are: \[
\begin{aligned}
P_\mathrm{HW}(AA) & = p^2 \\
P_\mathrm{HW}(Aa) & = 2p(1-p)\\
P_\mathrm{HW}(aa) & = (1-p)^2
\end{aligned}
\]
Basic probabilities
Inbreeding in a population violates one of the many assumptions
required for Hardy-Weinberg equilibrium to hold. We model inbreeding
here as a property of the pair of gene copies that occur in an
individual in the population, and we measure it with the population
inbreeding coefficient, \(f\), which
can be interpreted as the probability that the pair of gene copies in an
individual randomly sampled from the population are identical by
descent, or IBD for short. For our purposes in this session, we
will take “identical by descent” to mean that they are both copies of
the same, recent ancestral gene copy. More important than the exact
definition of “identical by descent” adopted here are the consequences
for two gene copies of being IBD. And in this case, that is very simple:
we will assume that if two gene copies are IBD, then they must also be
the same allelic type. In other words, if two gene copies are IBD, and
the first of them is allele \(A\), then
the second must also be allele \(A\).
So, given the inbreeding coefficient \(f\), we can compute the probability that a
randomly sampled individual has the genotype \(AA\), \(Aa\), or \(aa\). First, let us consider \(P(AA)\): with probability \(f\) the two gene copies at a locus in the
individual are IBD, in which case only the first gene copy must by \(A\), since the second one will be too. On
the other hand, with probability \((1-f)\) the two gene copies at the locus in
the individual are not IBD, and so the probability of being \(AA\) is just the standard Hardy-Weinberg
probability, \(P_\mathrm{HW}(AA) =
p^2\). Hence, under the inbreeding model: \[
\begin{aligned}
P(AA) &= fp + (1-f)p^2 \\
P(Aa) &= f\times 0 + (1-f)2p(1-p) \\
P(aa) &= f(1-p) + (1-f)(1-p)^2
\end{aligned}
\]
The likelihood
Now recall that our data are \((n_{AA},
n_{Aa}, n_{aa})\)—the numbers of the different genotypes observed
in the sample. To move forward in inference, we need to be able to
compute the likelihood, which is the probability of our data given the
values of the parameters: \(P(n_{AA}, n_{Aa},
n_{aa}|p,f)\). Note here that the two parameters in our model are
\(p\), the frequency of the \(A\) allele in the population, and \(f\), the population inbreeding coefficient.
We assume that each individual is sampled independently from the
population, so the likelihood is a multinomial: \[
P(n_{AA}, n_{Aa}, n_{aa}) = \frac{n!}{n_{AA}! + n_{Aa}! + n_{aa}!}~
[fp + (1-f)p^2]^{n_{AA}}~
[(1-f)2p(1-p)]^{n_{Aa}}~
[f(1-p) + (1-f)(1-p)^2]^{n_{aa}}
\] The multinomial coefficient is a constant with respect to
\(p\) and \(f\) so it will drop out.
The priors
We need priors of \(p\) and \(f\). These are proportions, so a natural
choice is the beta distribution.
Let’s define \(f \sim \mathrm{Beta}(\alpha_f,
\beta_f)\) and \(p \sim
\mathrm{Beta}(\alpha_p, \beta_p)\), and we will set up our model
that way, though the default values we will use are \(\alpha_f = \beta_f = \alpha_p = \beta_p =
1\) which will give us uniform priors for \(f\) and \(p\).
LS0tCnRpdGxlOiAiTUNNQyBTYW1wbGluZyBmb3IgYSBTaW1wbGUgSW5icmVlZGluZyBNb2RlbCIKZGVzY3JpcHRpb246IFdlIHJldmlldyBvdXIgc2ltcGxlIG1vZGVsIGZvciBpbmJyZWVkaW5nIGF0IGEgbG9jdXMgYW5kIGRldmVsb3AgdGhlIGNvZGUgdG8gcGVyZm9ybSBzZXZlcmFsIGRpZmZlcmVudCB0eXBlcyBvZiBNZXRyb3BvbGlzLUhhc3RpbmdzIHVwZGF0ZXMsIHRoZW4gYXVnbWVudCB0aGUgbW9kZWwgdG8gaW5jbHVkZSBsYXRlbnQgdmFyaWFibGVzIHRoYXQgbGV0IHVzIGVhc2lseSB0byBHaWJicy1zYW1wbGluZyB1cGRhdGVzLgpvdXRwdXQ6IAogIGh0bWxfbm90ZWJvb2s6CiAgICB0b2M6IHRydWUKICAgIHRvY19mbG9hdDogdHJ1ZQotLS0KClx1c2VwYWNrYWdle2Jsa2FycmF5fQpcdXNlcGFja2FnZXthbXNtYXRofQpcbmV3Y29tbWFuZHtcYm19e1xib2xkc3ltYm9sfQpcbmV3Y29tbWFuZHtcRXhwfXtcbWF0aGJiYntFfX0KClRoaXMgaXMgYW4gUi1jb2RlIGNvbXBhbmlvbiB0byBTZXNzaW9uIDcsIGFuZCB0aGUgY29udGVudHMgaGVyZSBlbmNvbXBhc3MganVzdCBhYm91dCAKYWxsIHRoZSBtYXRlcmlhbCBpbiB0aGF0IHNlc3Npb24uICBEdXJpbmcgdGhlIGxlY3R1cmUgYXNzb2NpYXRlZCB3aXRoIHRoZSBsZXNzb24sCndlIHNob3dlZCBzb21lIE9wZW5HTC1iYXNlZCBhbmltYXRpb25zIG9mIHRoZSBNQ01DIHNhbXBsZXIgZm9yIHRoZSBpbmJyZWVkaW5nIG1vZGVsLApidXQgdGhlIGNvZGUgZm9yIHRoYXQgaXMgaW1wbGVtZW50ZWQgaW4gQywgYW5kIGlzIG5vdCBzdXBlciBhY2Nlc3NpYmxlLiAgU28sIHRoZSBnb2FsCmhlcmUgaXMgdG8gaW1wbGVtZW50LCBpbiBSLCB0aGUgbW9kZWwgYW5kIE1DTUMgZm9yIHRoZSBtb2RlbCB1c2luZyB0aGUgc2V2ZXJhbCBkaWZmZXJlbnQKdHlwZXMgb2Ygc2FtcGxlcnMgZGlzY3Vzc2VkIGluIHRoZSBzZXNzaW9uLiAgCgpMZXQncyBsb2FkIEhhZGxleSdzIHRpZHl2ZXJzZSBhbmQgb3RoZXIgcGFja2FnZXMgd2UgbmVlZCBiZWZvcmUgd2UgZ2V0IGdvaW5nLiAgVGhlIGZvbGxvd2luZwp3aWxsIGFsc28gaW5zdGFsbCB0aGVtIGlmIHlvdSBkb24ndCBoYXZlIHRoZW0uIApgYGB7ciwgbWVzc2FnZT1GQUxTRX0KcGFja3MgPC0gYygidGlkeXZlcnNlIiwgInBsb3RseSIsICJ2aXJpZGlzIiwgImJyb29tIikKCiMgaW5zdGFsbCBhbnkgdGhhdCBhcmUgbm90IGhlcmUKaW5zdGFsbF9lbSA8LSBzZXRkaWZmKHBhY2tzLCByb3duYW1lcyhpbnN0YWxsZWQucGFja2FnZXMoKSkpCmlmIChsZW5ndGgoaW5zdGFsbF9lbSkgPiAwKSBpbnN0YWxsLnBhY2thZ2VzKHBrZ3MgPSBpbnN0YWxsX2VtKQoKIyBsb2FkIGVtIHVwCmR1bXAgPC0gbGFwcGx5KHBhY2tzLCBmdW5jdGlvbih4KSBsaWJyYXJ5KHgsIGNoYXJhY3Rlci5vbmx5ID0gVFJVRSkpCmBgYAoKCiMjIFRoZSBqb2ludCBwcm9iYWJpbGl0eQoKQXMgZGlzY3Vzc2VkIGluIHRoZSBwcmV2aW91cyBzZXNzaW9uLCB0aGlzIGlzIGp1c3QgdGhlIHByb2R1Y3Qgb2YgdGhlIHByaW9ycyBhbmQgdGhlIGxpa2VsaWhvb2RzLiAgSW4gaXRzIGZ1bGwgZ2xvcnksIGl0IGxvb2tzIGxpa2UuICAoRm9yIGEgcmV2aWV3IG9mIGhvdyB0aGF0IGpvaW50IGRlbnNpdHkgd2FzIGRldmVsb3BlZCwgc2VlClt0aGUgZGlzY3Vzc2lvbiBhdCB0aGUgdmVyeSBlbmRdKCNyZXZpZXcpCiQkClxiZWdpbnthbGlnbmVkfQpQKG5fe0FBfSwgbl97QWF9LCBuX3thYX0sIGYsIHAgfCBcYWxwaGFfZiwgXGJldGFfZiwgXGFscGhhX3AsIFxiZXRhX3ApICYgPSAKXGZyYWN7XEdhbW1hKFxhbHBoYV9mICsgXGJldGFfZil9e1xHYW1tYShcYWxwaGFfZilcR2FtbWEoXGJldGFfZil9Zl57XGFscGhhX2YgLSAxfSgxLWYpXntcYmV0YV9mIC0gMX0KXGZyYWN7XEdhbW1hKFxhbHBoYV9wICsgXGJldGFfcCl9e1xHYW1tYShcYWxwaGFfcClcR2FtbWEoXGJldGFfcCl9cF57XGFscGhhX3AgLSAxfSgxLXApXntcYmV0YV9wIC0gMX0gXHRpbWVzIFxcCiYgXGZyYWN7biF9e25fe0FBfSEgKyBuX3tBYX0hICsgbl97YWF9IX1+CltmcCArICgxLWYpcF4yXV57bl97QUF9fX4KWygxLWYpMnAoMS1wKV1ee25fe0FhfX1+CltmKDEtcCkgKyAoMS1mKSgxLXApXjJdXntuX3thYX19ClxlbmR7YWxpZ25lZH0KJCQKV2Ugd2lsbCBiZSB1c2luZyB0aGlzIGluIApNQ01DIGluIHdoaWNoIHRoZSB0aGUgaHlwZXJwcmlvcnMgJChcYWxwaGFfZiwgXGJldGFfZiwgXGFscGhhX3AsIFxiZXRhX3ApJCBhbmQgdGhlIGRhdGEgYXJlIGZpeGVkLgpUaHVzIHRoZSB0ZXJtcyAoZmFjdG9ycykgdGhhdCBpbnZvbHZlIF9vbmx5XyB0aGUgZGF0YSBhbmQgdGhlIGh5cGVycHJpb3JzIGFyZSBjb25zdGFudHMgdGhhdCB3aWxsIGRyb3AKb3V0IG9mIHRoZSBIYXN0aW5ncyByYXRpbywgc28gdGhleSBjYW4gYmUgc2FmZWx5IGlnbm9yZWQuICBTbywgb3VyIGpvaW50IHByb2JhYmlsaXR5IGNhbiBiZSBjb21wdXRlZAphcyBzb21ldGhpbmcgdGhhdCBsb29rcyBsaWtlOgokJApcYmVnaW57YWxpZ25lZH0KUChuX3tBQX0sIG5fe0FhfSwgbl97YWF9LCBmLCBwIHwgXGFscGhhX2YsIFxiZXRhX2YsIFxhbHBoYV9wLCBcYmV0YV9wKSAmIFxwcm9wdG8gCmZee1xhbHBoYV9mIC0gMX0oMS1mKV57XGJldGFfZiAtIDF9CnBee1xhbHBoYV9wIC0gMX0oMS1wKV57XGJldGFfcCAtIDF9IFx0aW1lcyBcXAomIH5+fn5bZnAgKyAoMS1mKXBeMl1ee25fe0FBfX1+ClsoMS1mKTJwKDEtcCldXntuX3tBYX19fgpbZigxLXApICsgKDEtZikoMS1wKV4yXV57bl97YWF9fQpcZW5ke2FsaWduZWR9CiQkCldlIHdpbGwgaW1wbGVtZW50IHRoaXMgaW4gYSBmdW5jdGlvbiB0aGF0IHJldHVybnMgdGhlIF9sb2dfIG9mIHRoZSBhYm92ZSBxdWFudGl0eQpzbyB0aGF0IHdlIHdvbid0IHJ1biBpbnRvIHVuZGVyZmxvdyBpc3N1ZXMgd2l0aCB2ZXJ5IGxhcmdlIHNhbXBsZXM6CmBgYHtyIGpvaW50fQojJyB0aGUgbG9nIG9mIHRoZSBqb2ludCBwcm9iYWJpbGl0eSBvZiBhbGwgdGhlIGRhdGEgYW5kIHBhcmFtYXRlcnMgZ2l2ZW4gdGhlIHByaW9ycwojJwojJyBUaGlzIGlzIGNvbXB1dGVkIGhhdmluZyBkcm9wcGVkIGFsbCB0aGUgY29uc3RhbnQgZmFjdG9ycyBpbiB0aGUgcHJvYmFiaWxpdHkuCiMnIEBwYXJhbSBuIGEgdmVjdG9yIG9mIHRocmVlIGVsZW1lbnRzOiAobkFBLCBuQWEsIG5hYSksIHRoZSBudW1iZXIgb2YgQUEncywgdGhlCiMnIG51bWJlciBvZiBBYSdzLCBhbmQgdGhlIG51bWJlciBvZiBhYSdzCiMnIEBwYXJhbSBmIHRoZSBpbmJyZWVkaW5nIGNvZWZmaWVudAojJyBAcGFyYW0gcCB0aGUgZnJlcXVlbmN5IG9mIHRoZSBBIGFsbGVsZQojJyBAcGFyYW0gcHJpIGEgdmVjdG9yIG9mIGZvdXIgY29tcG9uZW50czogKGFscGhhX2YsIGJldGFfZiwgYWxwaGFfcCwgYmV0YV9wKSB3aGljaCBhcmUgdGhlIAojJyBiZXRhIHBhcmFtZXRlcnMgZm9yIHRoZSBwcmlvcnMgb24gZiBhbmQgcC4gIEJ5IGRlZmF1bHQgdGhleSBhcmUgYWxsIDEuCmxvZ19qb2ludF9wcm9iIDwtIGZ1bmN0aW9uKG4sIGYsIHAsIHByaSA9IGMoMSwgMSwgMSwgMSkpIHsKICAjIG5vdCBnb2luZyB0byBkbyBtdWNoIGVycm9yIGNoZWNraW5nIGhlcmUKICAKICAjIGNvbXB1dGUgdGhlIHJhdyB2YWx1ZXMsIGFueXRoaW5nIG91dHNpZGUgb2YgdGhlIHVuaXQgaW50ZXJ2YWwgd2Ugd2lsbCBzZW5kCiAgIyBiYWNrIGFzIE5hTgogIGlmZWxzZShmIDw9IDAgfCBmID49IDEgfCBwIDw9IDAgfCBwID49IDEsIE5hTiwKICAgICAgICAgbG9nKGYpICogKHByaVsxXSAtIDEpICsgCiAgICAgICAgICAgbG9nKDEgLSBmKSAqIChwcmlbMl0gLSAxKSArCiAgICAgICAgICAgbG9nKHApICogKHByaVszXSAtIDEpICsKICAgICAgICAgICBsb2coMSAtIHApICogKHByaVs0XSAtIDEpICsKICAgICAgICAgICBsb2coZiAqIHAgKyAoMSAtIGYpICogcCBeIDIpICogblsxXSArCiAgICAgICAgICAgbG9nKCgxIC0gZikgKiAyICogcCAqICgxIC0gcCkpICogblsyXSArIAogICAgICAgICAgIGxvZyhmICogKDEgLSBwKSArICgxIC0gZikgKiAoMSAtIHApIF4gMikgKiBuWzNdKQogIAogIAoKfQpgYGAKTm90ZSB0aGF0IHRoaXMgZnVuY3Rpb24gaXMgdmVjdG9yaXplZCBvdmVyICRmJCBhbmQgJHAkLCBidXQgbm90ICRuJCBvciB0aGUgcHJpb3JzLgoKCiMjIyBBIDMtRCBwbG90CgpXZSBjYW4gY29tcHV0ZSB0aGUgam9pbnQgcHJvYmFiaWxpdHkgb2YgYWxsIHRoZSB2YXJpYWJsZXMgZWFzaWx5LCBhbmQsIGJlY2F1c2UgdGhpcyBpcyBhIHRveSBwcm9ibGVtIAppbiB0d28gZGltZW5zaW9ucywgd2UgY2FuIHZpc3VhbGl6ZSByb3VnaGx5IHdoYXQgdGhlIHBvc3RlcmlvciBzdXJmYWNlIHdpbGwgbG9vayBsaWtlIGJ5IApjb21wdXRpbmcgdmFsdWVzIG9uIGEgZ3JpZCBhbmQgbm9ybWFsaXppbmcgdGhvc2UgdmFsdWVzIHNvIHRoZXkgc3VtIHRvIG9uZS4gIExldCdzIGRvIHRoYXQsIHNpbmNlCml0IGlzIGEgZ29vZCBjaGFuY2UgdG8gaW50cm9kdWNlIHRoZSBbcGxvdGx5XShodHRwczovL3Bsb3QubHkvci8pIHBhY2thZ2UgdG8gYW55b25lIHRoYXQgbWlnaHQgbm90IHlldCBrbm93IGl0LgoKU28sIGxldCdzIHRha2UgdGhlIGV4YW1wbGUgZnJvbSB0aGUgbGVjdHVyZSBhbmQgc2V0IG91ciBkYXRhIHRvIGJlICRuX3tBQX0gPSAzMCQsICRuX3tBYX0gPSAxMCQsIGFuZCAKJG5fe2FhfSA9IDEwJC4gIFRob3NlIGFyZSB0aGUgZXhwZWN0ZWQgdmFsdWVzIGlmICRmID0gMC41JCBhbmQgJHAgPSAwLjckLApgYGB7cn0KbmRhdGEgPC0gYygzMCwgMTAsIDEwKQpgYGAKYW5kIHRoZW4gY29tcHV0ZSB0aGUgbG9nIHByb2IsIGV4cG9uZW50aWF0ZSBpdCwgYW5kIG5vcm1hbGl6ZSBpdCBvdmVyIGEgZ3JpZCBvZiB2YWx1ZSBmb3IKJGYkIGFuZCAkcCQ6CmBgYHtyfQpmX3ZhbHMgPC0gc2VxKDAsIDEsIGJ5ID0gMC4wMykKcF92YWxzIDwtIHNlcSgwLCAxLCBieSA9IDAuMDMpCgojIG1ha2UgYSB0aWR5IHRpYmJsZSBvZiB2YWx1ZXMgCmdyaWRfdmFscyA8LSBleHBhbmQuZ3JpZChmID0gZl92YWxzLCBwID0gcF92YWxzKSAlPiUKICBhc190aWJibGUoKSAlPiUKICBtdXRhdGUoam9pbnQgPSBleHAobG9nX2pvaW50X3Byb2IobmRhdGEsIGYsIHApKSkgJT4lCiAgbXV0YXRlKG5vcm1fam9pbnQgPSBqb2ludCAvIHN1bShqb2ludCwgbmEucm0gPSBUUlVFKSkgJT4lCiAgc2VsZWN0KC1qb2ludCkKCiMgdGhlbiBwdXQgdGhvc2UgdmFsdWVzIGludG8gYSBtYXRyaXgsIHdpdGggcm93cyBjb3JyZXNwb25kaW5nIHRvIGYgYW5kIGNvbHMgdG8gcApwcm9icyA8LSBtYXRyaXgoZ3JpZF92YWxzJG5vcm1fam9pbnQsIG5yb3cgPSBsZW5ndGgoZl92YWxzKSwgbmNvbCA9IGxlbmd0aChwX3ZhbHMpKQoKYGBgCk5vdyB3ZSBjYW4gcGxvdCBpdCB3aXRoIHBsb3RseToKYGBge3IsIGZpZy53aWR0aD04fQpwbG90X2x5KHggPSB+cF92YWxzLCB5ID0gfmZfdmFscywgeiA9IH5wcm9icykgJT4lCiAgYWRkX3N1cmZhY2UoKQpgYGAKClRyeSBpbnRlcmFjdGluZyB3aXRoIHRoYXQgcGxvdCwgZXNwZWNpYWxseSBpZiB5b3UgYXJlIG5vdCBmYW1pbGlhciB3aXRoIHBsb3RseS4gIFlvdSBjYW4gem9vbSwgcGFuLCBhbmQKcmV2b2x2ZSBpdCwgYW5kIHlvdSBjYW4gYWxzbyBsb29rIGF0IGluZGl2aWR1YWwgdmFsdWVzIGJ5IG1vdXNpbmcgb3ZlciB3aXRoaW4gdGhlIHBsb3QuCgojIyAiMi1EIiBNZXRyb3BvbGlzLUhhc3RpbmdzIFNhbXBsZXIKCldlIG5vdyBzdGFydCBvdXIgZm9yYXlzIGludG8gZGlmZmVyZW50IE1DTUMgc2FtcGxlcnMsIHJvdWdobHkgZm9sbG93aW5nIHRoZSBsZWN0dXJlIG5vdGVzIGZyb20gdGhlIHNlc3Npb24uIApUaGUgZmlyc3QgaXMgYSBzYW1wbGVyIGluIHdoaWNoIHdlIHByb3Bvc2UgY2hhbmdlcyB0byBib3RoICRmJCBhbmQgJHAkIGFuZCB0aGVuIHdlIGFjY2VwdCBvciByZWplY3QgYm90aCBvZiB0aG9zZQpwcm9wb3NhbGVkIGNoYW5nZXMsIHRvZ2V0aGVyLCBpbiBvbmUgZmVsbCBzd29vcCwgYWNjb3JkaW5nIHRvIHRoZSBIYXN0aW5ncyBSYXRpby4KCkZvciBvdXIgcHJvcG9zYWxzIGluIHRoaXMgY2FzZSB3ZSB3aWxsIHVzZSB0d28gaW5kZXBlbmRlbnQsIHN5bW1tZXRyaWNhbCBub3JtYWwgZGlzdHJpYnV0aW9ucyBjZW50ZXJlZApvbiB0aGUgY3VycmVudCB2YWx1ZXMuICBXZSB3aWxsIHdyYXAgdXAgdGhlIHdob2xlIHByb2Nlc3MgaW4gYSBzaW5nbGUgZnVuY3Rpb24KY2FsbGVkIGBtY21jXzJkX21oKClgLgpgYGB7cn0KIycgZnVuY3Rpb24gdG8gZG8gTUNNQyB1c2luZyBhICIyLUQiIE1ldHJvcG9saXMtSGFzdGluZ3Mgc2FtcGxlcgojJyBAcGFyYW0gbmRhdGEgYSB0aHJlZS12ZWN0b3IgKG5BQSwgbkFhLCBuYWEpIG9mIHRoZSBvYnNlcnZlZCBkYXRhCiMnIEBwYXJhbSBwcmkgYSB2ZWN0b3Igb2YgZm91ciBjb21wb25lbnRzOiAoYWxwaGFfZiwgYmV0YV9mLCBhbHBoYV9wLCBiZXRhX3ApCiMnIEBwYXJhbSBpbml0IGEgdHdvIHZlY3RvciBvZiBzdGFydGluZyB2YWx1ZXMgKGYsIHApLiAgRWFjaCBzaG91bGQgYmUgd2l0aGluIHRoZQojJyB1bml0IGludGVydmFsLgojJyBAcGFyYW0gc3dlZXBzIHRoZSBudW1iZXIgb2Ygc3dlZXBzIChpLmUgcHJvcG9zZWQgY2hhbmdlcykgdG8gZG8gaW4gdGhlIE1DTUMgY2hhaW4uCiMnIEBwYXJhbSBmX3NkIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gZm9yIHByb3Bvc2luZyBuZXcgdmFsdWVzIHRvIGYKIycgQHBhcmFtIHBfc2QgdGhlIHN0YW5kYXJkIGRldmlhdGlvbiBvZiB0aGUgbm9ybWFsIGRpc3RyaWJ1dGlvbiBmb3IgcHJvcG9zaW5nIG5ldyB2YWx1ZXMgdG8gcAojJyBAcmV0dXJuIHJldHVybnMgYSB0aWJibGUgb2YgcHJvcG9zYWxzIG1hZGUsIHZhbHVlcyB2aXNpdGVkLCB2YWx1ZXMgb2YgTUggcmF0aW8sIGV0YwptY21jXzJkX21oIDwtIGZ1bmN0aW9uKG5kYXRhLCAKICAgICAgICAgICAgICAgICAgICAgICBwcmkgPSBjKDEsIDEsIDEsIDEpLCAKICAgICAgICAgICAgICAgICAgICAgICBpbml0ID0gYyguMiwgLjUpLCAKICAgICAgICAgICAgICAgICAgICAgICBzd2VlcHMgPSA1MDAsCiAgICAgICAgICAgICAgICAgICAgICAgZl9zZCA9IDAuMDcsCiAgICAgICAgICAgICAgICAgICAgICAgcF9zZCA9IDAuMDcpIHsKCiAgIyBjcmVhdGUgdmVjdG9ycyBpbiB3aGljaCB0byBzdG9yZSBvdXRwdXQuIE5vdGUgdGhhdCB3ZSB3aWxsIGJlIHN0b3JpbmcgCiAgIyB0aGUgcHJvcG9zZWQgdmFsdWVzIGFuZCB0aGUgTUgtcmF0aW8gZWFjaCBzd2VlcCwgdG9vLCBmb3IgbGF0ZXIgYW5hbHlzaXMuCiAgZiA8LSBwIDwtIGZwcm9wIDwtIHBwcm9wIDwtIG1oX3JhdGlvIDwtIHJlcChOQV9yZWFsXywgc3dlZXBzICsgMSkKICBhY2NlcHRlZF9mIDwtIGFjY2VwdGVkX3AgPC0gcmVwKE5BLCBzd2VlcHMgKyAxKQogIAogICMgcHV0IGluaXRpYWwgdmFsdWVzIGluCiAgZlsxXSA8LSBpbml0WzFdCiAgcFsxXSA8LSBpbml0WzJdCiAgCiAgIyB0aGVuIGN5Y2xlIG92ZXIgc3dlZXBzCiAgZm9yIChpIGluIDE6c3dlZXBzKSB7CiAgICAKICAgICMgcHJvcG9zZSBuZXcgdmFsdWVzCiAgICBmcHJvcFtpXSA8LSBybm9ybSgxLCBmW2ldLCBmX3NkKQogICAgcHByb3BbaV0gPC0gcm5vcm0oMSwgcFtpXSwgcF9zZCkKICAgIAogICAgIyBjb21wdXRlIHRoZSBoYXN0aW5ncyByYXRpbwogICAgIyB0aGUgbG9nIE0tSCByYXRpbyBpcyB0aGUgZGlmZmVyZW5jZSBvZiB0aGUgbG9nIGpvaW50IHByb2JzLgogICAgIyBzaW5jZSB0aGUgcHJvcG9zYWwgZGlzdHJpYnV0aW9ucyBhcmUgc3ltbWV0cmljYWwgdGhleSBkcm9wIG91dCBvZiB0aGUgcmF0aW8uCiAgICBtaF9yYXRpb1tpXSA8LSBleHAobG9nX2pvaW50X3Byb2IobmRhdGEsIGZwcm9wW2ldLCBwcHJvcFtpXSwgcHJpKSAtCiAgICAgIGxvZ19qb2ludF9wcm9iKG5kYXRhLCBmW2ldLCBwW2ldLCBwcmkpKQogICAgCiAgICAjIG5vdywgaWYgbWhfcmF0aW9baV0gaXMgTmFOIGl0IG1lYW5zIHRoYXQgZiBvciBwIHdhcyBwcm9wb3NlZCBvdXRzaWRlIG9mIHRoZSB1bml0IGludGVydmFsCiAgICAjIGFuZCBpdCBzaG91bGQgYmUgcmVqZWN0ZWQgaW1tZWRpYXRlbHkuICBJZiBub3QsIHRoZW4gd2Ugc2ltdWxhdGUgYSB1bmlmb3JtIFIuVi4gb24gKDAsIDEpCiAgICAjIGFuZCByZWplY3QgdGhlIHByb3Bvc2FsIGlmIHRoYXQgbnVtYmVyIGlzIGdyZWF0ZXIgdGhhbiB0aGUgTUgtcmF0aW8uCiAgICBpZiAoaXMubmFuKG1oX3JhdGlvW2ldKSB8fCBydW5pZigxKSA+IG1oX3JhdGlvW2ldKSB7IyByZWplY3QhCiAgICAgIGFjY2VwdGVkX2ZbaV0gPC0gRkFMU0UKICAgICAgYWNjZXB0ZWRfcFtpXSA8LSBGQUxTRQogICAgICBmW2kgKyAxXSA8LSBmW2ldCiAgICAgIHBbaSArIDFdIDwtIHBbaV0KICAgIH0gZWxzZSB7IyBhY2NlcHQhCiAgICAgIGFjY2VwdGVkX2ZbaV0gPC0gVFJVRQogICAgICBhY2NlcHRlZF9wW2ldIDwtIFRSVUUKICAgICAgZltpICsgMV0gPC0gZnByb3BbaV0KICAgICAgcFtpICsgMV0gPC0gcHByb3BbaV0KICAgIH0KICAgIAogIH0KICAKICAjIGluIHRoZSBlbmQsIG1ha2UgYSB0aWJibGUgYW5kIHJldHVybgogIHRpYmJsZShzd2VlcCA9IDE6KHN3ZWVwcyArIDEpLAogICAgICAgICBmID0gZiwKICAgICAgICAgcCA9IHAsIAogICAgICAgICBwcm9wb3NlZF9mID0gZnByb3AsCiAgICAgICAgIHByb3Bvc2VkX3AgPSBwcHJvcCwKICAgICAgICAgbWhfcmF0aW8gPSBtaF9yYXRpbywKICAgICAgICAgYWNjZXB0ZWRfZiA9IGFjY2VwdGVkX2YsCiAgICAgICAgIGFjY2VwdGVkX3AgPSBhY2NlcHRlZF9wCiAgICAgICAgICkKfQpgYGAKCioqQSBicmllZiBub3RlOioqIEluIHRoZSBhYm92ZSBmdW5jdGlvbiAoYW5kIHRoZSBvbmUgZm9sbG93aW5nKSB3ZSBhcmUgYmVpbmcgY2FyZWZ1bCB0byByZWNvcmQKbm90IGp1c3QgdGhlIGN1cnJlbnQgc3RhdGUgb2YgdGhlIGNoYWluLCBidXQgYWxzbwpldmVyeSBfcHJvcG9zZWRfIHZhbHVlLiAgVHlwaWNhbGx5IHRoaXMgaXMgbm90IGRvbmUgaW4gTUNNQy4gIFJhdGhlciwgaXQgaXMgbW9yZSB1c3VhbCB0byBzaW1wbHkKcmVjb3JkIHRoZSBzdGF0ZSBvZiB0aGUgY2hhaW4gYXQgZWFjaCBzd2VlcCAob3IgYXQgc29tZSAidGhpbm5lZCIgaW50ZXJ2YWwgb2Ygc3dlZXBzKS4gSG93ZXZlcgp3ZSB3YW50ZWQgdG8ga2VlcCBhIGZ1bGwgcmVjb3JkIG9mIHRoZSBwcm9ncmVzc2lvbiBvZiBhbGwgdGhlIHZhcmlhYmxlcyBzbyB0aGF0IHRoZXkgd291bGQgYmUKYXZhaWxhYmxlIGZvciAgc3R1ZGVudHMgdG8gcGVydXNlIG9yIGFuYWx5emUuIAoKCiMjIENvbXBvbmVudC13aXNlIE1ldHJvcG9saXMtSGFzdGluZ3MgU2FtcGxlcgoKUmF0aGVyIHRoYW4gcHJvcG9zaW5nIGNoYW5nZXMgdG8gYm90aCAkZiQgYW5kICRwJCBiZWZvcmUgYWNjZXB0aW5nIG9yIHJlamVjdGluZyB0aGVtLAp3ZSBjYW4gcHJvcG9zZSBqdXN0IGEgY2hhbmdlIHRvICRmJCAoc2F5KSBhbmQgYWNjZXB0IG9yIHJlamVjdCB0aGUgcHJvcG9zYWwsIGFuZCB0aGVuIHByb3Bvc2UganVzdCBhIGNoYW5nZQp0byAkcCQsIGFuZCB0aGVuIGFjY2VwdCBvciByZWplY3QgdGhhdC4gIFRoaXMgaXMgY2FsbGVkIGNvbXBvbmVudC13aXNlIE1ldHJvcG9saXMtSGFzdGluZ3Mgc2FtcGxpbmcuIApVbmRlcmx5aW5nIGl0IGlzIGEgY2VudHJhbCB0aGVtZSBpbiBNQ01DLCB3aGljaCBpcyB0aGF0IGp1c3Qgb25lIG9yIGEgZmV3IGRpbWVuc2lvbnMgY2FuIGJlIGNoYW5nZWQKd2l0aCBlYWNoIHByb3Bvc2FsLCBidXQgYXMgbG9uZyBhcyB0aGUgYWNjZXB0YW5jZSBvciByZWplY3Rpb24gb2YgZWFjaCBwcm9wb3NhbCBpcyBkb25lIGluCmEgd2F5IHRoYXQgc2F0aXNmaWVzIGRldGFpbGVkIGJhbGFuY2UsIF9hbmRfIGFzIGxvbmcgYXMgYWxsIHRoZSBkaWZmZXJlbnQgdHlwZXMgb2YgcHJvcG9zYWxzLAp3aGVuIHVzZWQgdG9nZXRoZXIgY3JlYXRlIGFuIGlycmVkdWNpYmxlIGNoYWluIGFyb3VuZCB0aGUgc3BhY2UsIHRoZW4geW91IGdldCBhIHZhbGlkIE1DTUMgc2FtcGxlci4KCldlIG1ha2UgYSBmdW5jdGlvbiB0aGF0IGxvb2tzIG11Y2ggbGlrZSBgbWNtY18yZF9taCgpYCwgYnV0IGluIHdoaWNoIHRoZSBwcm9wb3NhbCBhbmQgYWNjZXB0YW5jZSBzdGVwcwpmb3IgJGYkIGFuZCAkcCQgYXJlIGRvbmUgc2VwYXJhdGVseSB3aXRoaW4gZWFjaCBzd2VlcC4gVGhpcyBpcyBnb2luZyB0byBsb29rIGEgbGl0dGxlIGJpdCB3ZWlyZApiZWNhdXNlIHdlIGRvIGVhY2ggdXBkYXRlIHdpdGhpbiB0aGUgc3dlZXAgYnV0IHNldCB0aGUgbmV3IHZhbHVlIG9mICRmJCBpbiB0aGUgIm5leHQiIHN3ZWVwLgooWW91J2xsIHNlZSBiZWxvdy4uLikKYGBge3J9CiMnIGZ1bmN0aW9uIHRvIGRvIE1DTUMgdXNpbmcgYSBjb21wb25lbnQtd2lzZSBNZXRyb3BvbGlzLUhhc3RpbmdzIHNhbXBsZXIKIycgQHBhcmFtIG5kYXRhIGEgdGhyZWUtdmVjdG9yIChuQUEsIG5BYSwgbmFhKSBvZiB0aGUgb2JzZXJ2ZWQgZGF0YQojJyBAcGFyYW0gcHJpIGEgdmVjdG9yIG9mIGZvdXIgY29tcG9uZW50czogKGFscGhhX2YsIGJldGFfZiwgYWxwaGFfcCwgYmV0YV9wKQojJyBAcGFyYW0gaW5pdCBhIHR3byB2ZWN0b3Igb2Ygc3RhcnRpbmcgdmFsdWVzIChmLCBwKS4gIEVhY2ggc2hvdWxkIGJlIHdpdGhpbiB0aGUKIycgdW5pdCBpbnRlcnZhbC4KIycgQHBhcmFtIHN3ZWVwcyB0aGUgbnVtYmVyIG9mIHN3ZWVwcyAoaS5lIHByb3Bvc2VkIGNoYW5nZXMpIHRvIGRvIGluIHRoZSBNQ01DIGNoYWluLgojJyBAcGFyYW0gZl9zZCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9uIG9mIHRoZSBub3JtYWwgZGlzdHJpYnV0aW9uIGZvciBwcm9wb3NpbmcgbmV3IHZhbHVlcyB0byBmCiMnIEBwYXJhbSBwX3NkIHRoZSBzdGFuZGFyZCBkZXZpYXRpb24gb2YgdGhlIG5vcm1hbCBkaXN0cmlidXRpb24gZm9yIHByb3Bvc2luZyBuZXcgdmFsdWVzIHRvIHAKIycgQHJldHVybiByZXR1cm5zIGEgdGliYmxlIG9mIHByb3Bvc2FscyBtYWRlLCB2YWx1ZXMgdmlzaXRlZCwgdmFsdWVzIG9mIE1IIHJhdGlvLCBldGMKbWNtY19jd19taCA8LSBmdW5jdGlvbihuZGF0YSwgCiAgICAgICAgICAgICAgICAgICAgICAgcHJpID0gYygxLCAxLCAxLCAxKSwgCiAgICAgICAgICAgICAgICAgICAgICAgaW5pdCA9IGMoLjIsIC41KSwgCiAgICAgICAgICAgICAgICAgICAgICAgc3dlZXBzID0gNTAwLAogICAgICAgICAgICAgICAgICAgICAgIGZfc2QgPSAwLjA3LAogICAgICAgICAgICAgICAgICAgICAgIHBfc2QgPSAwLjA3KSB7CgogICMgY3JlYXRlIHZlY3RvcnMgaW4gd2hpY2ggdG8gc3RvcmUgb3V0cHV0LiBOb3RlIHRoYXQgd2Ugd2lsbCBiZSBzdG9yaW5nIAogICMgdGhlIHByb3Bvc2VkIHZhbHVlcyBhbmQgdGhlIE1ILXJhdGlvIGVhY2ggc3dlZXAsIHRvbywgZm9yIGxhdGVyIGFuYWx5c2lzLgogIGYgPC0gcCA8LSBmcHJvcCA8LSBwcHJvcCA8LSBtaF9yYXRpb19mIDwtIG1oX3JhdGlvX3AgPC0gcmVwKE5BX3JlYWxfLCBzd2VlcHMgKyAxKQogIGFjY2VwdGVkX2YgPC0gYWNjZXB0ZWRfcCA8LSByZXAoTkEsIHN3ZWVwcyArIDEpCiAgCiAgIyBwdXQgaW5pdGlhbCB2YWx1ZXMgaW4KICBmWzFdIDwtIGluaXRbMV0KICBwWzFdIDwtIGluaXRbMl0KICAKICAjIHRoZW4gY3ljbGUgb3ZlciBzd2VlcHMKICBmb3IgKGkgaW4gMTpzd2VlcHMpIHsKICAgIAogICAgIyBwcm9wb3NlIG5ldyB2YWx1ZSBmb3IgZgogICAgZnByb3BbaV0gPC0gcm5vcm0oMSwgZltpXSwgZl9zZCkKICAgIAogICAgIyBjb21wdXRlIHRoZSBoYXN0aW5ncyByYXRpbyB3aXRoIG9ubHkgYSBjaGFuZ2UgdG8gZiBwcm9wb3NlZAogICAgbWhfcmF0aW9fZltpXSA8LSBleHAobG9nX2pvaW50X3Byb2IobmRhdGEsIGZwcm9wW2ldLCBwW2ldLCBwcmkpIC0KICAgICAgbG9nX2pvaW50X3Byb2IobmRhdGEsIGZbaV0sIHBbaV0sIHByaSkpCiAgICAKICAgICMgdGhlbiBhY2NlcHQgb3IgcmVqZWN0IGl0CiAgICBVIDwtIHJ1bmlmKDEpCiAgICBpZiAoaXMubmFuKG1oX3JhdGlvX2ZbaV0pIHwgcnVuaWYoMSkgPiBtaF9yYXRpb19mW2ldKSB7IyByZWplY3QhCiAgICAgIGFjY2VwdGVkX2ZbaV0gPC0gRkFMU0UKICAgICAgZltpICsgMV0gPC0gZltpXQogICAgfSBlbHNlIHsjIGlmIGFjY2VwdGVkLCB0aGVuIHVwZGF0ZSB0aGUgdmFsdWUgb2YgZgogICAgICBhY2NlcHRlZF9mW2ldIDwtIFRSVUUKICAgICAgZltpICsgMV0gPC0gZnByb3BbaV0gIyBzZXQgdGhlIHZhbHVlLiAgVGhpcyBpcyBhIGxpdHRsZSB3ZWlyZCBiZWNhdXNlIHdlIGFyZSBzdGlsbCBpbiBzd2VlcCBpLgogICAgfQogICAgCiAgICAjIG5vdywgdGhlIGN1cnJlbnQgdmFsdWUgb2YgZiBpcyBzdG9yZWQgaW4gZltpICsgMV0sIGFuZCB3ZSBwcm9wb3NlIGEgbmV3IHZhbHVlIGZvciBwCiAgICBwcHJvcFtpXSA8LSBybm9ybSgxLCBwW2ldLCBwX3NkKQogICAgCiAgICAgIyBjb21wdXRlIHRoZSBoYXN0aW5ncyByYXRpbyB1c2luZyB0aGUgbmV3IGYgYW5kIHRoZSBwcm9wb3NlZCB2YWx1ZSBvZiBwCiAgICBtaF9yYXRpb19wW2ldIDwtIGV4cChsb2dfam9pbnRfcHJvYihuZGF0YSwgZltpICsgMV0sIHBwcm9wW2ldLCBwcmkpIC0KICAgICAgbG9nX2pvaW50X3Byb2IobmRhdGEsIGZbaSArIDFdLCBwW2ldLCBwcmkpKQogICAgCiAgICAKICAgICMgYW5kIHJlamVjdCBvciBhY2NlcHQgdGhlIHByb3Bvc2FsCiAgICBpZiAoaXMubmFuKG1oX3JhdGlvX3BbaV0pIHwgcnVuaWYoMSkgPiBtaF9yYXRpb19wW2ldKSB7IyByZWplY3QhCiAgICAgIGFjY2VwdGVkX3BbaV0gPC0gRkFMU0UKICAgICAgcFtpICsgMV0gPC0gcFtpXQogICAgfSBlbHNlIHsjIGlmIG5vdCBhY2NlcHRlZCB0aGVuIG1ha2UgcCB0aGUgc2FtZSBhcyB0aGUgY3VycmVudCBwCiAgICAgIGFjY2VwdGVkX3BbaV0gPC0gVFJVRQogICAgICBwW2kgKyAxXSA8LSBwcHJvcFtpXQogICAgfQogICAgCiAgfQogIAogICMgaW4gdGhlIGVuZCwgbWFrZSBhIHRpYmJsZSBhbmQgcmV0dXJuCiAgdGliYmxlKHN3ZWVwID0gMTooc3dlZXBzICsgMSksCiAgICAgICAgIGYgPSBmLAogICAgICAgICBwID0gcCwgCiAgICAgICAgIHByb3Bvc2VkX2YgPSBmcHJvcCwKICAgICAgICAgcHJvcG9zZWRfcCA9IHBwcm9wLAogICAgICAgICBtaF9yYXRpb19mID0gbWhfcmF0aW9fZiwKICAgICAgICAgbWhfcmF0aW9fcCA9IG1oX3JhdGlvX3AsCiAgICAgICAgIGFjY2VwdGVkX2YgPSBhY2NlcHRlZF9mLAogICAgICAgICBhY2NlcHRlZF9wID0gYWNjZXB0ZWRfcAogICAgICAgICApCn0KYGBgCgoKIyMgVXNpbmcgdGhlIHNhbXBsZXJzCgpFYWNoIG9mIHRoZXNlIE1ldHJvcG9saXMtSGFzdGluZ3Mgc2FtcGxlcnMgcmV0dXJucyBvdXRwdXQgaW4gYSB0aWJibGUuIEZvciBleGFtcGxlOgpgYGB7cn0KbWNtY19jd19taChuZGF0YSA9IGMoMzAsIDEwLCAxMCkpCmBgYAoKIyMgRXhlcmNpc2VzCgoxLiBSdW4gZWFjaCBvZiB0aGUgc2FtcGxlcnMgZm9yIDUwMDAgc3dlZXBzLCB3aXRoIGBuZGF0YSA9IGMoMzAsIDEwLCAxMClgLgoyLiBQbG90IHRoZSB0cmFjZXMgZm9yICRmJCBhbmQgJHAkIGZvciBib3RoIHRoZSAyLWQgYW5kIHRoZSBjb21wb25lbnR3aXNlIHNhbXBsZXIuIEFyZSB0aGV5IHZpc2libHkgZGlmZmVyZW50PwozLiBDb21wYXJlIHRoZSBhY2NlcHRhbmNlIHJhdGVzIGZvciAkcCQgYW5kICRmJCBmb3IgYm90aCBzYW1wbGVycy4gIAo0LiBJbiAzIHlvdSBzaG91bGQgaGF2ZSBmb3VuZCB0aGF0IHRoZSBhY2NlcHRhbmNlIHJhdGUgaXMgaGlnaGVyIGluIHRoZSBjb21wb25lbnQtd2lzZSBzYW1wbGVyIChhcyBleHBlY3RlZCkuCjUuIFRoaXMgd2FzIGEgc2ltcGxlIGV4ZXJjaXNlIGluIHNhbXBsaW5nIGNvbnRpbnVvdXMgcmFuZG9tIHZhcmlhYmxlcyBhY2NvcmRpbmcgdG8gYSB0d28gZGltZW5zaW9uYWwKcG9zdGVyaW9yIHN1cmZhY2UuICBOb3csIGltYWdpbmUgdGhhdCB0aGUgc3RhbmRhcmQgZGV2aWF0aW9ucyBvZiB0aGUgcHJvcG9zYWwgZGlzdHJpYnV0aW9ucyBmb3IgJHAkIGFuZCAkZiQgYXJlIGZpeGVkLCB0aGVuLCBkcmF3IChieSBoYW5kKSB0aGUgbGV2ZWwgY3VydmVzIChhICJjb250b3VyIiBwbG90KSBvZiBhIDItZCBwb3N0ZXJpb3Igc3VyZmFjZSBmb3Igd2hpY2ggdGhlCjItZCBzYW1wbGVyIG1pZ2h0IGhhdmUgYSBoaWdoZXIgYWNjZXB0YW5jZSByYXRlIHRoYW4gdGhlIGNvbXBvbmVudCB3aWRlIHNhbXBsZXIuICBEcmF3IHRoZSBzdGFuZGFyZCBkZXZpYXRpb25zCm9mIHRoZSBwcm9wb3NhbCBkaXN0cmlidXRpb25zIG9uIGZvciBzY2FsZS4KCgojIyBSZXZpZXcgb2YgdGhlIG1vZGVsIHsjcmV2aWV3fQoKV2UgYXJlIGRlYWxpbmcgd2l0aCBhIHZlcnkgc2ltcGxlIG1vZGVsIGZvciBpbmJyZWVkaW5nLiAgSXQgaXMgb25lIHR5cGUgb2YKbW9kZWwgdGhhdCBtaWdodCBiZSB1c2VkIHRvIGFjY291bnQgZm9yIGFuIGV4Y2VzcyBvZiBob21venlnb3RlcyBpbiBhIHNhbXBsZSAKZnJvbSBhIHBvcHVsYXRpb24uICBXZSBhc3N1bWUgdGhhdCB3ZSBoYXZlIHRha2VuIGEgc2FtcGxlIG9mICRuJCBkaXBsb2lkCmluZGl2aWR1YWxzIGZyb20gdGhlIHBvcHVsYXRpb24sIGFuZCB3ZSB3aWxsIGJlIGNvbnNpZGVyaW5nIGp1c3QgYSBzaW5nbGUKbG9jdXMgYXQgYSB0aW1lLiAgIFRoaXMgbG9jdXMgaGFzIHR3byBhbGxlbGVzICRBJCBhbmQgJGEkLCBhbmQgb3VyIHNhbXBsZSAKY2FuIGJlIHN1bW1hcml6ZWQgYnkgKHdoYXQgdHVybnMgb3V0IHRvIGJlIHRoZSBzdWZmaWNpZW50IHN0YXRpc3RpYywgaW4gdGhpcwpjYXNlKSB0aGUgbnVtYmVycyBvZiB0aGUgdGhyZWUgcG9zc2libGUgZ2Vub3R5cGVzOiAkbl97QUF9JCwgdGhlIG51bWJlciBvZgokQUEkIGhvbW96eWdvdGVzOyAkbl97QWF9JCwgdGhlIG51bWJlciBvZiBoZXRlcm96eWdvdGVzOyBhbmQgJG5fe2FhfSQsIHRoZQpudW1iZXIgb2YgJGFhJCBob21venlnb3Rlcy4gIE9mIGNvdXJzZSwgJG5fe0FBfSArIG5fe0FhfSArIG5fe2FhfSA9IG4kLgoKSW4gYSBwb3B1bGF0aW9uIHdpdGggbm8gaW5icmVlZGluZywgdGhlIGV4cGVjdGVkIGZyYWN0aW9uIG9mIGdlbm90eXBlcyBmb2xsb3dzCnRoZSBIYXJkeS1XZWluYmVyZyBlcXVpbGlicml1bSBwcm9wb3J0aW9ucy4gIExldHRpbmcgJHAkIGRlbm90ZSB0aGUgZnJlcXVlbmN5IG9mIHRoZQokQSQgYWxsZWxlIGluIHRoZSBwb3B1bGF0aW9uLCB1bmRlciBIYXJkeS1XZWluYmVyZyBlcXVpbGlicml1bSB0aGUsIGV4cGVjdGVkIGZyYWN0aW9ucwpvZiB0aGUgZGlmZmVyZW50IGdlbm90eXBlcyBhcmU6CiQkClxiZWdpbnthbGlnbmVkfQpQX1xtYXRocm17SFd9KEFBKSAmID0gcF4yIFxcClBfXG1hdGhybXtIV30oQWEpICYgPSAycCgxLXApXFwKUF9cbWF0aHJte0hXfShhYSkgJiA9ICgxLXApXjIKXGVuZHthbGlnbmVkfQokJAoKIyMjIEJhc2ljIHByb2JhYmlsaXRpZXMKCkluYnJlZWRpbmcgaW4gYSBwb3B1bGF0aW9uIHZpb2xhdGVzIG9uZSBvZiB0aGUgbWFueSBhc3N1bXB0aW9ucyByZXF1aXJlZCBmb3IgSGFyZHktV2VpbmJlcmcKZXF1aWxpYnJpdW0gdG8gaG9sZC4gIFdlIG1vZGVsIGluYnJlZWRpbmcgaGVyZSBhcyBhIHByb3BlcnR5IG9mIHRoZSBwYWlyIG9mIGdlbmUgY29waWVzCnRoYXQgb2NjdXIgaW4gYW4gaW5kaXZpZHVhbCBpbiB0aGUgcG9wdWxhdGlvbiwgYW5kIHdlIG1lYXN1cmUgaXQgd2l0aCB0aGUgcG9wdWxhdGlvbgppbmJyZWVkaW5nIGNvZWZmaWNpZW50LCAkZiQsIHdoaWNoIGNhbiBiZSBpbnRlcnByZXRlZCBhcyB0aGUgcHJvYmFiaWxpdHkKdGhhdCB0aGUgcGFpciBvZiBnZW5lIGNvcGllcyBpbiBhbiBpbmRpdmlkdWFsIHJhbmRvbWx5IHNhbXBsZWQgZnJvbSB0aGUgcG9wdWxhdGlvbiBhcmUgX2lkZW50aWNhbApieSBkZXNjZW50Xywgb3IgSUJEIGZvciBzaG9ydC4gIEZvciBvdXIgcHVycG9zZXMgaW4gdGhpcyBzZXNzaW9uLCB3ZSB3aWxsIHRha2UgImlkZW50aWNhbCBieSBkZXNjZW50IiB0byBtZWFuIHRoYXQKdGhleSBhcmUgYm90aCBjb3BpZXMgb2YgdGhlIHNhbWUsIHJlY2VudCBhbmNlc3RyYWwgZ2VuZSBjb3B5LiAgTW9yZSBpbXBvcnRhbnQgdGhhbiB0aGUgZXhhY3QKZGVmaW5pdGlvbiBvZiAiaWRlbnRpY2FsIGJ5IGRlc2NlbnQiIGFkb3B0ZWQgaGVyZSBhcmUgdGhlIGNvbnNlcXVlbmNlcyBmb3IgdHdvIGdlbmUgY29waWVzIG9mCmJlaW5nIElCRC4gIEFuZCBpbiB0aGlzIGNhc2UsIHRoYXQgaXMgdmVyeSBzaW1wbGU6IHdlIHdpbGwgYXNzdW1lIHRoYXQgaWYgCnR3byBnZW5lIGNvcGllcyBhcmUgSUJELCB0aGVuIHRoZXkgbXVzdCBhbHNvIGJlIHRoZSBzYW1lIGFsbGVsaWMgdHlwZS4gIEluIG90aGVyCndvcmRzLCBpZiB0d28gZ2VuZSBjb3BpZXMgYXJlIElCRCwgYW5kIHRoZSBmaXJzdCBvZiB0aGVtIGlzIGFsbGVsZSAkQSQsIHRoZW4gdGhlIHNlY29uZCBtdXN0CmFsc28gYmUgYWxsZWxlICRBJC4gIAoKU28sIGdpdmVuIHRoZSBpbmJyZWVkaW5nIGNvZWZmaWNpZW50ICRmJCwgd2UgY2FuIGNvbXB1dGUgdGhlIHByb2JhYmlsaXR5IHRoYXQKYSByYW5kb21seSBzYW1wbGVkIGluZGl2aWR1YWwgaGFzIHRoZSBnZW5vdHlwZSAkQUEkLCAkQWEkLCBvciAkYWEkLiAgRmlyc3QsIGxldCB1cyBjb25zaWRlcgokUChBQSkkOiB3aXRoIHByb2JhYmlsaXR5ICRmJCB0aGUgdHdvIGdlbmUgY29waWVzIGF0IGEgbG9jdXMgaW4gdGhlIGluZGl2aWR1YWwgYXJlIElCRCwgCmluIHdoaWNoIGNhc2Ugb25seSB0aGUgZmlyc3QgZ2VuZSBjb3B5IG11c3QgYnkgJEEkLCBzaW5jZSB0aGUgc2Vjb25kIG9uZSB3aWxsIGJlIHRvby4gIE9uIHRoZSBvdGhlciBoYW5kLCB3aXRoIHByb2JhYmlsaXR5ICQoMS1mKSQgdGhlIHR3byBnZW5lIGNvcGllcyBhdCB0aGUgbG9jdXMgaW4gdGhlCmluZGl2aWR1YWwgYXJlIG5vdCBJQkQsIGFuZCBzbyB0aGUgcHJvYmFiaWxpdHkgb2YgYmVpbmcgJEFBJCBpcyBqdXN0IHRoZSBzdGFuZGFyZCBIYXJkeS1XZWluYmVyZyAKcHJvYmFiaWxpdHksICRQX1xtYXRocm17SFd9KEFBKSA9IHBeMiQuICBIZW5jZSwgdW5kZXIgdGhlIGluYnJlZWRpbmcgbW9kZWw6CiQkClxiZWdpbnthbGlnbmVkfQpQKEFBKSAmPSBmcCArICgxLWYpcF4yIFxcClAoQWEpICY9IGZcdGltZXMgMCArICgxLWYpMnAoMS1wKSBcXApQKGFhKSAmPSBmKDEtcCkgKyAoMS1mKSgxLXApXjIKXGVuZHthbGlnbmVkfQokJAoKIyMjIFRoZSBsaWtlbGlob29kCk5vdyByZWNhbGwgdGhhdCBvdXIgZGF0YSBhcmUgJChuX3tBQX0sIG5fe0FhfSwgbl97YWF9KSQtLS10aGUgbnVtYmVycyBvZiB0aGUgZGlmZmVyZW50IGdlbm90eXBlcwpvYnNlcnZlZCBpbiB0aGUgc2FtcGxlLiAgVG8gbW92ZSBmb3J3YXJkIGluIGluZmVyZW5jZSwgd2UgbmVlZCB0byBiZSBhYmxlIHRvIGNvbXB1dGUgdGhlIApsaWtlbGlob29kLCB3aGljaCBpcyB0aGUgcHJvYmFiaWxpdHkgb2Ygb3VyIGRhdGEgZ2l2ZW4gdGhlIHZhbHVlcyBvZiB0aGUgcGFyYW1ldGVyczogJFAobl97QUF9LCBuX3tBYX0sIG5fe2FhfXxwLGYpJC4KTm90ZSBoZXJlIHRoYXQgdGhlIHR3byBwYXJhbWV0ZXJzIGluIG91ciBtb2RlbCBhcmUgJHAkLCB0aGUgZnJlcXVlbmN5IG9mIHRoZSAkQSQgYWxsZWxlIGluIHRoZSBwb3B1bGF0aW9uLAphbmQgJGYkLCB0aGUgcG9wdWxhdGlvbiBpbmJyZWVkaW5nIGNvZWZmaWNpZW50LiAgV2UgYXNzdW1lIHRoYXQgZWFjaCBpbmRpdmlkdWFsIGlzIHNhbXBsZWQgaW5kZXBlbmRlbnRseQpmcm9tIHRoZSBwb3B1bGF0aW9uLCBzbyB0aGUgbGlrZWxpaG9vZCBpcyBhIG11bHRpbm9taWFsOgokJApQKG5fe0FBfSwgbl97QWF9LCBuX3thYX0pID0gXGZyYWN7biF9e25fe0FBfSEgKyBuX3tBYX0hICsgbl97YWF9IX1+CltmcCArICgxLWYpcF4yXV57bl97QUF9fX4KWygxLWYpMnAoMS1wKV1ee25fe0FhfX1+CltmKDEtcCkgKyAoMS1mKSgxLXApXjJdXntuX3thYX19CiQkClRoZSBtdWx0aW5vbWlhbCBjb2VmZmljaWVudCBpcyBhIGNvbnN0YW50IHdpdGggcmVzcGVjdCB0byAkcCQgYW5kICRmJCBzbyBpdCB3aWxsIGRyb3Agb3V0LiAgCgojIyMgVGhlIHByaW9ycwoKV2UgbmVlZCBwcmlvcnMgb2YgJHAkIGFuZCAkZiQuICBUaGVzZSBhcmUgcHJvcG9ydGlvbnMsIHNvIGEgbmF0dXJhbCBjaG9pY2UgaXMgdGhlIGJldGEgZGlzdHJpYnV0aW9uLiAgCkxldCdzIGRlZmluZSAkZiBcc2ltIFxtYXRocm17QmV0YX0oXGFscGhhX2YsIFxiZXRhX2YpJCBhbmQgJHAgXHNpbSBcbWF0aHJte0JldGF9KFxhbHBoYV9wLCBcYmV0YV9wKSQsCmFuZCB3ZSB3aWxsIHNldCB1cCBvdXIgbW9kZWwgdGhhdCB3YXksIHRob3VnaCB0aGUgZGVmYXVsdCB2YWx1ZXMgd2Ugd2lsbCB1c2UgYXJlCiRcYWxwaGFfZiA9IFxiZXRhX2YgPSBcYWxwaGFfcCA9IFxiZXRhX3AgPSAxJCB3aGljaCB3aWxsIGdpdmUgdXMgdW5pZm9ybSBwcmlvcnMgZm9yICRmJCBhbmQgJHAkLiAgCg==